#include "AI.h"
#include "../../Games/TyrianEnvironment.h"
#include "../../Games/MyGame.h"
#include <limits> 
#include <fstream>
#include <iostream>

AI::AI(int maxFrequency, std::string name) : GameEntity(maxFrequency, name){
}

AI::~AI() {
}

void AI::doSomething() {
	sense();										//Sense opponent(s), locations, distances etc...
	reason();										//Plan a next move according to a pathplanner or simple hill climbing
	if(plan.size() > 0) {							//If the plan is not empty
		act(plan.front());							//act on the first element in the plan
		plan.pop_front();							//and remove it after the action is done
	}
}

int AI::bestImprovement() {
	float maxEval = evaluate(getPosition());
	int bestAction = -1;
	std::vector<int>::iterator it;		
	POINT3D p;
	for(it=moveActions.begin(); it != moveActions.end(); ++it) {	//Test all possible actions
		int action = (*it);
		p = simulateMove(getPosition(), action);					//simulate the action
		if(MyGame::Instance()->getEnv()->isLegalLocation(p)) {
			float eval = evaluate(p);								//evaluate it
			if(eval > maxEval) {									//if it is better than the maximum action until now, save it
				maxEval = eval;
				bestAction = action;
			}
		}
	}
	return bestAction;
}

int AI::firstImprovement() {
	float curEval = evaluate(getPosition());
	std::vector<int>::iterator it;		
	POINT3D p;
	for(it=moveActions.begin(); it != moveActions.end(); ++it) { //Test all possible actions
		int action (*it);
		p = simulateMove(getPosition(), action);					//simulate the action
		float eval = evaluate(p);								//evaluate it
		if(eval > curEval) 										//if it is better than the current position:
			return action;										//return the action
	}
	return -1;
}

void AI::planPath(array1d *distanceMap) {
	plan.clear();
	bool finished = false;												// is the goal reached
	POINT3D p = MyGame::Instance()->getEnv()->gameCoord(GameEntity::getPosition());// current position
	POINT3D p2;															// candidate position
	
	while(!finished){	
		int distance = distanceMap[(int)p.x][(int)p.y];
		std::vector<int>::iterator it2;		
		for(it2=moveActions.begin(); it2 != moveActions.end(); ++it2) {	//Test all possible move actions
			int action = (*it2);
			p2 = simulateMove(p,action);
			int newDistance = distanceMap[(int)p2.x][(int)p2.y];
			if(newDistance < distance) {								//if the new distance is closer that the current
				plan.push_back(action);									//insert it to the plan
				p = p2;													//Check from the new point on the next iteration
				if(newDistance == 0) finished = true;					//break when last move goes to 0
			}
		}
	}
}

void AI::createDistanceMap(POINT3D goal, array1d * distanceMap){
	for(int i = 0; i < 7; i++) 
		for(int j = 0; j < 5; j++) 
				distanceMap[i][j] = 1000;//std::numeric_limits<int>::max();
	
	POINT3D p;	
	front.clear();	
	newFront.clear();		

	front.push_back(MyGame::Instance()->getEnv()->gameCoord(goal));		// Start with goal
	int w = 0;															// The distance (=weight) of the goal to the goal if of course 0
	
	do {
		std::vector<POINT3D>::iterator it;									
		for(it=front.begin(); it != front.end(); ++it) {				//iterate over the entire front
			p = (*it);
			distanceMap[(int)p.x][(int)p.y] = w;						// Update the point in the distance map
			
			std::vector<POINT3D>::iterator it2;		
			addToFront(p, w, distanceMap, &newFront);					// Add all reachable points from p to the new front
		}
		front.clear();													// clear front  
		front.insert(front.end(), newFront.begin(), newFront.end());	// front = new front
		newFront.clear();												// clear new front as well
		w++;															// And increment w
	} while(front.size()!=0);										// while there is still a front to explore
	
}


void AI::addToFront(POINT3D p, int g, array1d *distanceMap, std::vector<POINT3D> * f) {
	std::vector<int>::iterator it;						
	POINT3D p2;				
	for(it=moveActions.begin(); it != moveActions.end(); ++it) {	//Test all possible move actions
		int action = (*it);
		p2 = simulateMove(p, action);
		bool b;
		if(p2.x > -1 && p2.x < 37 && p2.y > -1 && p2.y < 27 /*b = MyGame::Instance()->getEnv()->isLegalLocation(MyGame::Instance()->getEnv()->realCoord(p2)) */ && distanceMap[(int)p2.x][(int)p2.y] > g + 1) 	//If the position is not in the distancemap yet and the move is to a legal location...
			f->push_back(p2);										//...add point to reachables
	}
}